import socket

from threading import Semaphore, Thread
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QMessageBox

from libs.pyqt5braisedpork.errorMap import Code, CodeMap
from libs.utils import timer_for_scanner


class PortScannerSingleThread(Thread):
    """端口扫描器 单个线程"""

    def __init__(self, host, port, signals):
        super().__init__()
        self.host = host
        self.port = port
        self.signals = signals

    def emit_port_status(self):
        """发送单个端口的开启信息"""
        try:
            socket.setdefaulttimeout(0.5)
            session = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            session.connect((self.host, self.port))
            session.close()

            self.signals["set_port_status"].emit(
                f"<p style='margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px;"
                f" -qt-block-indent:0; text-indent:0px;'><span>&nbsp;&nbsp;*端口<span style='color:green'>"
                f"[{self.port}]</span></span> <span style='color=green'>开启</span></p>"
            )
        except OSError:
            pass

    def run(self) -> None:
        self.emit_port_status()


class PortScannerThread(QThread):
    """端口扫描器类"""
    set_port_status = pyqtSignal(str)               # 端口状态打印信号

    def __init__(self, window):
        super().__init__()
        self.window = window
        self.signals = {"set_port_status": self.set_port_status}  # 定义信号表给子线程使用

    def run(self) -> None:
        self.port_scan_runner()

    @timer_for_scanner
    def port_scan_runner(self):
        """按范围进行扫描的流程控制"""
        try:
            # 1. 获取扫描主机地址
            scan_ip = self.window.lineEdit_scanIp.text()
            port_range_text = self.window.lineEdit_portRange.text()

            if not self.window.check_not_null((scan_ip, port_range_text)):
                return QMessageBox.information(self.window, Code.NotNullError, CodeMap[Code.NotNullError])

            # 2. 获取扫描端口范围元组
            port_range = tuple([int(i) for i in self.window.lineEdit_portRange.text().split(",")])
            max_thread = Semaphore(self.window.horizontalSlider_threadNum.value())

            # 3. 开启扫描
            mission_list = []

            for port in range(*port_range):
                with max_thread:  # 限制最大并发线程数量
                    single_thread = PortScannerSingleThread(scan_ip, port, self.signals)  # 下发信号表下发给子线程
                    mission_list.append(single_thread)
                    single_thread.start()

            for mission in mission_list:  # 守护进程 (实际上这里守护的是主线程)
                mission.join()

        except Exception as e:
            return QMessageBox.critical(self, f"{Code.UnknownError}", f"{CodeMap[Code.UnknownError]} {str(e)}")

